package net.qiqbframework.modelling.domain;


import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.messaging.annotation.*;
import org.javers.core.JaversBuilder;
import org.javers.core.metamodel.clazz.EntityDefinitionBuilder;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class AnnotatedAggregateModelFactory implements AggregateModelFactory {

    private final Map<Class<?>, AnnotatedAggregateModel> registry;
    private final Map<String, AnnotatedAggregateModel> namingRegistry;

    private final HandlerParameterResolverFactory parameterResolverFactory;
    private final MessageHandlingMemberFactory handlerDefinition;

    private final JaversBuilder javersBuilder;


    public static <T> AggregateModel<T> inspectAggregate(Class<T> aggregateType, Set<Class<? extends T>> subtypes, JaversBuilder javersBuilder) {
        return new AnnotatedAggregateModelFactory(javersBuilder).createModel(aggregateType, subtypes);
    }

    public static <T> AggregateModel<T> inspectAggregate(Class<T> aggregateType,
                                                         HandlerParameterResolverFactory parameterResolverFactory, JaversBuilder javersBuilder) {
        return new AnnotatedAggregateModelFactory(parameterResolverFactory, javersBuilder).createModel(aggregateType);
    }


    public static <T> AggregateModel<T> inspectAggregate(Class<T> aggregateType,
                                                         HandlerParameterResolverFactory parameterResolverFactory,
                                                         MessageHandlingMemberFactory handlerDefinition, JaversBuilder javersBuilder) {
        return new AnnotatedAggregateModelFactory(parameterResolverFactory, handlerDefinition, javersBuilder)
                .createModel(aggregateType);
    }

    public static <T> AggregateModel<T> inspectAggregate(Class<T> aggregateType,
                                                         HandlerParameterResolverFactory parameterResolverFactory,
                                                         MessageHandlingMemberFactory handlerDefinition,
                                                         Set<Class<? extends T>> subtypes, JaversBuilder javersBuilder) {
        return new AnnotatedAggregateModelFactory(parameterResolverFactory, handlerDefinition, javersBuilder)
                .createModel(aggregateType, subtypes);
    }


    public AnnotatedAggregateModelFactory(JaversBuilder javersBuilder) {
        this(ClasspathParameterResolverFactory.forClassLoader(Thread.currentThread().getContextClassLoader()), javersBuilder);
    }

    /**
     * Initializes an instance which uses the given {@code parameterResolverFactory} to detect parameters for annotated
     * handlers.
     *
     * @param parameterResolverFactory to resolve parameter values of annotated handlers with
     */
    public AnnotatedAggregateModelFactory(HandlerParameterResolverFactory parameterResolverFactory, JaversBuilder javersBuilder) {
        this(parameterResolverFactory,
                ClasspathHandlerDefinition.forClassLoader(Thread.currentThread().getContextClassLoader()), javersBuilder);
    }

    /**
     * Initializes an instance which uses the given {@code parameterResolverFactory} to detect parameters for annotated
     * handlers and {@code handlerDefinition} to create concrete handlers.
     *
     * @param parameterResolverFactory to resolve parameter values of annotated handlers with
     * @param handlerDefinition        The handler definition used to create concrete handlers
     */
    public AnnotatedAggregateModelFactory(HandlerParameterResolverFactory parameterResolverFactory,
                                          MessageHandlingMemberFactory handlerDefinition, JaversBuilder javersBuilder) {
        this.parameterResolverFactory = parameterResolverFactory;
        this.handlerDefinition = handlerDefinition;
        this.javersBuilder = javersBuilder;
        registry = new ConcurrentHashMap<>();
        namingRegistry = new ConcurrentHashMap<>();
    }

    @Override
    public <T> AnnotatedAggregateModel<T> createModel(Class<? extends T> aggregateType,
                                                      Set<Class<? extends T>> subtypes) {
        if (!registry.containsKey(aggregateType)) {
            AnnotatedHandlerInspector<T> inspector = AnnotatedHandlerInspector.inspectType(aggregateType,
                    parameterResolverFactory,
                    HandlerAttributesPostProcessor.EMPTY,
                    handlerDefinition,
                    subtypes);
            AnnotatedAggregateModel<T> model = new AnnotatedAggregateModel<>(aggregateType, inspector);
            registry.put(aggregateType, model);
            model.initialize();
            if (namingRegistry.containsKey(model.modelName())) {
                throw new AggregateModellingException("名称%s冲突".formatted(model.modelName()));
            }
            if (javersBuilder != null) {

                final EntityDefinitionBuilder entityDefinitionBuilder = EntityDefinitionBuilder.entityDefinition(aggregateType);
                entityDefinitionBuilder.withIdPropertyName(model.getEntityIdModel().modelName());
                entityDefinitionBuilder.withTypeName(model.modelLabel());
                javersBuilder.registerEntity(entityDefinitionBuilder.build());
            }

            namingRegistry.put(model.modelName(), model);
        }
        // noinspection unchecked
        return registry.get(aggregateType).whenReadSafe();
    }

    @Override
    public <T> Optional<AggregateModel<T>> getModel(String aggregateNaming) {
        return Optional.ofNullable(namingRegistry.get(aggregateNaming));
    }

    @Override
    public <T> Optional<AggregateModel<T>> getModel(Class<T> aggregateRootType) {
        return Optional.ofNullable(registry.get(aggregateRootType));
    }

    @Override
    public Set<String> getAllAggregateNaming() {
        return namingRegistry.keySet();
    }
}
